package wsdc.app.main.v2;// HttpServerHandler.java
import cn.hutool.core.util.StrUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.*;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.AttributeKey;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import wsdc.app.main.SpringContext;
import wsdc.app.main.U;
import wsdc.app.main.v1.CertConfig;

import javax.net.ssl.SSLSession;
import java.io.File;
import java.security.cert.Certificate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * HTTP服务器处理器，处理HTTP请求并返回响应
 */
@Slf4j
public class HttpServerHandler extends ChannelInboundHandlerAdapter {
    boolean ssl = false;

    static OkHttpClient client = new OkHttpClient();
    public HttpServerHandler proxyUrl(String url){
        ssl = U.ssl(url);
        return this;
    }
    
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if(true && msg instanceof FullHttpRequest){
            FullHttpRequest request = (FullHttpRequest) msg;
            String url = ssl ? "https://" : "http";
            // 获取请求信息 host里面是有端口信息 不需要额外处理
            String host = request.headers().get(HttpHeaderNames.HOST);
            url += host;
            String uri = request.uri();
            url = uri.startsWith("http") ? uri : url+uri;
            final String url1 = url;
            HttpMethod method = request.method();
            log.info("请求地址 {} 方法 {}",url1, method.name());



            Thread.ofVirtual().start(() -> {
                if("OPTIONS".equalsIgnoreCase(method.name())){
                    handleOptionsRequest(ctx, request);
                    return ;
                }
                if("GET".equalsIgnoreCase(method.name()) || "DELETE".equalsIgnoreCase(method.name()) ){
                    Request.Builder b = new Request.Builder();
                    if(!url1.startsWith("http")){
                        int k= 0;
                    }
                    b.url(url1);
                    request.headers().forEach((e) -> {
                        b.header(e.getKey(),e.getValue());
                    });
                    if("GET".equalsIgnoreCase(method.name())){
                        b.get();
                    } else if ("DELETE".equalsIgnoreCase(method.name())) {
                        b.delete();
                    }
                    Request req = b.build();
                    try{
                        Response res = client.newCall(req).execute();
                        byte[] bytes = res.body().bytes();

                        //  注意要调整Content-Length 因为期间可能压缩过内容 造成实际长度和请求头的长度不一样  还有 transfer-encoding: chunked
                        FullHttpResponse r1 = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(bytes));
                        Map<String, List<String>> multimap = res.headers().toMultimap();
                        multimap.forEach((k,v) -> {
                            if(StrUtil.equalsAnyIgnoreCase("Content-Length") || StrUtil.equalsAnyIgnoreCase("transfer-encoding")){
                                return ;
                            }
                            r1.headers().add(k,joinWith(v));
                        });
                        r1.headers().add("Content-Length", bytes.length);

                        String ct = res.headers().get("Content-Type");
                        if(ct != null && ct.contains("image")){
                            CertConfig config = SpringContext.context().getBean(CertConfig.class);
                            File dir = new File(config.getDir()+"/"+"img");
                            DateTimeFormatter dtf = DateTimeFormatter.ofPattern("YYYY-MM-dd");
                            File dir1 = new File(dir,dtf.format(LocalDateTime.now()));

                            saveImageResource(dir1,url1,bytes,ct);
                        }

                        ctx.executor().execute(() -> {
                            ctx.writeAndFlush(r1);
                        });

                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }

                if("POST".equalsIgnoreCase(method.name()) || "PUT".equalsIgnoreCase(method.name())){
                    Request.Builder b = new Request.Builder();
                    b.url(url1);
                    request.headers().forEach((e) -> {
                        b.header(e.getKey(),e.getValue());
                    });
                    //  暂时不处理文件上传的问题
                    String contentType = request.headers().get("Content-Type");
                    contentType = contentType == null ? "" : contentType;
                    byte[] data = ProxyUtils.readBuf(request.content()).toByteArray();
                    RequestBody body = RequestBody.create(MediaType.parse(contentType), data);
                    if("POST".equalsIgnoreCase(method.name())){
                        b.post(body);
                    }else{
                        b.put(body);
                    }
                    Request req = b.build();
                    try{
                        Response res = client.newCall(req).execute();
                        byte[] bytes = res.body().bytes();

                        //  注意要调整Content-Length 因为期间可能压缩过内容 造成实际长度和请求头的长度不一样  还有 transfer-encoding: chunked
                        FullHttpResponse r1 = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(bytes));
                        Map<String, List<String>> multimap = res.headers().toMultimap();
                        multimap.forEach((k,v) -> {
                            if(StrUtil.equalsAnyIgnoreCase("Content-Length") || StrUtil.equalsAnyIgnoreCase("transfer-encoding")){
                                return ;
                            }
                            r1.headers().add(k,joinWith(v));
                        });
                        r1.headers().add("Content-Length", bytes.length);




                        ctx.executor().execute(() -> {
                            ctx.writeAndFlush(r1);
                        });

                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            });

            return ;
        }
    }

    private void saveImageResource(File dir,String url, byte[] imageData, String contentType) {
        try {
            if (!dir.exists()) {
                dir.mkdirs();
            }

            // 处理文件名：去掉http/https前缀，并使用content-type的后缀
            String fileName = url.replaceAll("^https?://", "").replaceAll("[^a-zA-Z0-9._-]", "_");
            String extension = contentType.substring(contentType.indexOf("/") + 1);

            if(!isImageName(fileName)){
                fileName = fileName + "." + extension;
            }

            // 写入文件
            java.nio.file.Path filePath = java.nio.file.Paths.get(dir.getAbsolutePath(), fileName);
            java.nio.file.Files.write(filePath, imageData);

            log.info("Saved image to: {}", filePath.toString());
        } catch (Exception e) {
            log.error("Failed to save image resource: {}", url, e);
        }
    }

    boolean isImageName(String fileName){
        List<String> ls = new LinkedList();
        ls.add("svg");
        ls.add("png");
        ls.add("jpg");
        ls.add("jpeg");
        ls.add("gif");
        ls.add("webp");

        for (String l : ls) {
            if(fileName.endsWith(l)){
                return true;
            }
        }
        return false;
    }
    
    /**
     * 获取SSL连接信息
     * 
     * @param ctx ChannelHandlerContext
     * @return SSL信息字符串
     */
    private String getSslInfo(ChannelHandlerContext ctx) {
        try {
            SslHandler sslHandler = ctx.pipeline().get(SslHandler.class);
            if (sslHandler != null) {
                SSLSession session = sslHandler.engine().getSession();
                Certificate[] localCerts = session.getLocalCertificates();
                if (localCerts != null && localCerts.length > 0) {
                    return "Certificate loaded successfully";
                }
            }
        } catch (Exception e) {
            // 忽略异常
        }
        return "No SSL information";
    }
    
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) throws Exception {
        String origin = ctx.channel().attr(ContextKey.g).get();
        log.info("12121221 {}",origin,e);
        ctx.close();
    }

    public static String joinWith(List<String> list) {
        // 处理空指针情况
        if (list == null || list.isEmpty()) {
            return "";
        }

        // 过滤掉null元素并用分号连接
        return list.stream()
                .filter(item -> item != null) // 过滤掉null元素
                .collect(Collectors.joining(";"));
    }

    private void handleOptionsRequest(ChannelHandlerContext ctx, FullHttpRequest request) {
        FullHttpResponse response = new DefaultFullHttpResponse(
                HttpVersion.HTTP_1_1,
                HttpResponseStatus.OK);

        // 设置CORS头部
        response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
        response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, PUT, DELETE, OPTIONS");
        response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS, "*");
        response.headers().set(HttpHeaderNames.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
        response.headers().set(HttpHeaderNames.ACCESS_CONTROL_MAX_AGE, "1728000");
        response.headers().set(HttpHeaderNames.ALLOW, "GET, POST, PUT, DELETE, OPTIONS");
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0);

        // 处理Keep-Alive
        if (HttpUtil.isKeepAlive(request)) {
            response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
        }

        // 发送响应
        ctx.executor().execute(() -> {
            ctx.writeAndFlush(response);
        });
    }
}
